%%% Copyright 2010 Google Inc.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%%     http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.

%%% @copyright 2010 Google Inc. All Rights Reserved.
%%% @author <jacoby@google.com> Jacoby Thwaites [http://sites/madcloud]
%%% @version {@version}
%%% @doc
%%% Tests for name-ref set dict module {@link nr_set_dict}.
%%% @see nr_set_dict
%%%

-module(nr_set_dict_tests).

-include_lib("eunit/include/eunit.hrl").

nr_set_dict_test_() ->
   {setup,

    % Create the test set dict.
    fun() ->
         nr_set_dict:new()
    end,

    % No teardown
    fun(_NRD) ->
         ok
    end,

    % Test generator takes constructed nr_set_dict.
    fun(NRD) ->
         [empty_tests(NRD),
          from_tests(),
          populated_tests(NRD)]
    end}.

%% Basic tests using nr_set_dict created at setup time.
empty_tests(NRD) ->
  [
    % Test empty dictionary.
    fun() ->
         ?assertEqual(0, nr_set_dict:count_sets(NRD)),
         ?assertEqual(0, nr_set_dict:count_refs(NRD))
    end,

    % Test not found return.
    fun() ->
         ?assertEqual('$not_found', nr_set_dict:get_nr_set("foo", NRD))
    end,

    % Test empty list of names.
    fun() ->
        ?assertEqual([], nr_set_dict:get_nr_set_names(NRD))
    end,

    % Test empty union set.
    fun() ->
         ?assertEqual(0, sets:size(nr_set_dict:ref_union_set(NRD)))
    end
  ].

%% Tests creation of nr_set_dicts from proplists and dicts.
from_tests() ->
  [
    % Test creation from a proplist of proplists.
    fun() ->
         NRD = nr_set_dict:from_proplist([{"SetOne",
                                           [{"one", {folder, {1, 0, 0}}},
                                            {"two", {op, {2, 0, 0}}},
                                            {"three", {field, {3, 0, 0}}},
                                            {"four", {op, {4, 0, 0}}}]},
                                          {"SetTwo",
                                           [{"one", {folder, {1, 0, 0}}},
                                            {"two2", {op, {2, 0, 0}}},
                                            {"three2", {field, {3, 0, 1}}}]},
                                          {"SetThree",
                                           [{"one", {folder, {1, 0, 0}}},
                                            {"two", {op, {2, 0, 0}}},
                                            {"three3", {field, {3, 0, 0}}},
                                            {"four3", {op, {4, 0, 0}}}]}]),
         ?assertEqual(3, nr_set_dict:count_sets(NRD)),
         ?assertEqual(5, nr_set_dict:count_refs(NRD))
    end,

    % Test creation from a dict of dicts.
    fun() ->
         DictOfDicts = dict:from_list([{"SetOne",
                                        dict:from_list(
                                          [{"one", {folder, {1, 0, 0}}},
                                           {"two", {op, {2, 0, 0}}},
                                           {"three", {field, {3, 0, 0}}},
                                           {"four", {op, {4, 0, 0}}}])},
                                       {"SetTwo",
                                        dict:from_list(
                                          [{"one", {folder, {1, 0, 0}}},
                                           {"two2", {op, {2, 0, 0}}},
                                           {"three2", {field, {3, 0, 1}}}])},
                                       {"SetThree",
                                        dict:from_list(
                                          [{"one", {folder, {1, 0, 0}}},
                                           {"two", {op, {2, 0, 0}}},
                                           {"three3", {field, {3, 0, 0}}},
                                           {"four3", {op, {4, 0, 0}}}])}]),
         NRD = nr_set_dict:from_dict(DictOfDicts),
         ?assertEqual(3, nr_set_dict:count_sets(NRD)),
         ?assertEqual(5, nr_set_dict:count_refs(NRD))
    end
    ].

%% Tests on populated nr_set_dict.
populated_tests(NRD) ->
  [
    % Test single set store.
    fun() ->
         NRD1 = nr_set_dict:store_nr_set("one", setOne(), NRD),
         ?assertEqual(1, nr_set_dict:count_sets(NRD1)),
         ?assertEqual(4, nr_set_dict:count_refs(NRD1)),
         RetrievedSetOne = nr_set_dict:get_nr_set("one", NRD1),
         ?assertEqual(4, nr_set:count(RetrievedSetOne))
    end,

    % Test two set store.
    fun() ->
         NRD1 = nr_set_dict:store_nr_set("one", setOne(), NRD),
         NRD2 = nr_set_dict:store_nr_set("two", setTwo(), NRD1),
         ?assertEqual(2, nr_set_dict:count_sets(NRD2)),
         ?assertEqual(5, nr_set_dict:count_refs(NRD2)),
         RetrievedSetTwo = nr_set_dict:get_nr_set("two", NRD2),
         ?assertEqual(3, nr_set:count(RetrievedSetTwo))
    end,

    % Test two set union.
    fun() ->
         NRD1 = nr_set_dict:store_nr_set("one", setOne(), NRD),
         NRD2 = nr_set_dict:store_nr_set("two", setTwo(), NRD1),
         Union = nr_set_dict:ref_union_set(NRD2),
         ?assertEqual(5, sets:size(Union)),
         ?assert(sets:is_element({op, {4, 0, 0}}, Union))
    end,

    % Test three set union.
    fun() ->
         NRD1 = nr_set_dict:store_nr_set("one", setOne(), NRD),
         NRD2 = nr_set_dict:store_nr_set("two", setTwo(), NRD1),
         NRD3 = nr_set_dict:store_nr_set("three", setThree(), NRD2),
         Union = nr_set_dict:ref_union_set(NRD3),
         ?assertEqual(5, sets:size(Union)),
         ?assert(sets:is_element({field, {3, 0, 1}}, Union))
    end,


    % Test overwriting existing nr_set does not leave orphan ref in union set.
    fun() ->
         NRD = nr_set_dict:new(),
         NRD1 = nr_set_dict:store_nr_set("two", setTwo(), NRD),
         NRD2 = nr_set_dict:store_nr_set("three", setThree(), NRD1),
         NRD3 = nr_set_dict:store_nr_set("three", setThreeA(), NRD2),
         Union = nr_set_dict:ref_union_set(NRD3),
         ?assertEqual(5, sets:size(Union)),
         ?assert(not(sets:is_element({op, {4, 0, 0}}, Union))),
         ?assert(sets:is_element({op, {4, 0, 1}}, Union))
    end
    ].

%% Utility functions.
setOne() -> nr_set:from_proplist([{"one", {folder, {1, 0, 0}}},
                {"two", {op, {2, 0, 0}}},
                {"three", {field, {3, 0, 0}}},
                {"four", {op, {4, 0, 0}}}]).

setTwo() -> nr_set:from_proplist([{"one", {folder, {1, 0, 0}}},
                {"two2", {op, {2, 0, 0}}},
                {"three2", {field, {3, 0, 1}}}]).

setThree() -> nr_set:from_proplist([{"one", {folder, {1, 0, 0}}},
                {"two", {op, {2, 0, 0}}},
                {"three3", {field, {3, 0, 0}}},
                {"four3", {op, {4, 0, 0}}}]).

setThreeA() -> nr_set:from_proplist([{"one", {folder, {1, 0, 0}}},
                {"two", {op, {2, 0, 0}}},
                {"three3", {field, {3, 0, 0}}},
                {"four3a", {op, {4, 0, 1}}}]).
